/*
 * This file is part of ddesktop: remote desktop system for iPad 
 *
 * Copyright (C) 2010 Kazuhiro Satoh
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. if not, see <http://www.gnu.org/licenses/>.
 */


#include "stdafx.h"
#include "ServerController.h"

#pragma unmanaged

#include <windows.h>
#include <string.h>
#include <stdio.h>

#include "LibJpegEncoder.h"

namespace DDesktopServer{

	ServerController::ServerController(void)
	{
		m_capture = new DesktopCapture();
		m_encoder = new LibJpegEncoder();
		m_server_port = new DDServerPort();
		m_event_handler = new UIEventHandler();
		m_frame_ct = 0;
		m_frame_x = 0;
		m_frame_y = 0;
		m_frame_width = 1024;
		m_frame_height = 768;
		m_image_quality = 50;
		m_image_resolution = 1;
		m_capture_thread_handle = NULL;
		m_observer_window = NULL;
	}

	ServerController::~ServerController()
	{
		Close();
		delete m_capture;
		delete m_encoder;
		delete m_server_port;
		delete m_event_handler;
	}


	bool ServerController::Init(int port)
	{
		bool ok = true;

		int screen_width = ::GetSystemMetrics(SM_CXSCREEN);
		int screen_height = ::GetSystemMetrics(SM_CYSCREEN);



		ok &= m_capture->Init(screen_width, screen_height);
		ok &= m_encoder->Init(screen_width, screen_height);
		ok &= m_server_port->Init(port, this);
		ok &= CreateCaptureThread();

		return ok;
	}

	bool ServerController::Close(void)
	{
		bool ok = true;
		ok &= TerminateCaptureThread();
		ok &= m_server_port->Close();

		return ok;
	}


	void ServerController::AddObserverWindow(HWND hwnd)
	{
		m_observer_window = hwnd;
		//m_observer_windows.push_back(hwnd);
	}

	void ServerController::PostMessageToObservers(UINT msg, WPARAM wparam, LPARAM lparam)
	{
		//unsigned int i;
		//for(i = 0; i < m_observer_windows.size(); i++){
			::PostMessage(m_observer_window, msg, wparam, lparam);
		//}
	}

	const DDServerPort* ServerController::GetDDServerPort(void) const
	{
		return m_server_port;
	}


	int ServerController::GetFrameCt(void) const
	{
		return m_frame_ct;
	}

	int ServerController::GetImageQuality(void)const
	{
		return m_image_quality;
	}

	int ServerController::GetImageResolution(void)const
	{
		return m_image_resolution;
	}

	void ServerController::GetCaptureRect(RECT* rect)
	{
		rect->left   = m_frame_x;
		rect->top    = m_frame_y;
		rect->right  = m_frame_x + m_frame_width;
		rect->bottom = m_frame_y + m_frame_height;
	}


	bool ServerController::CreateCaptureThread(void)
	{
		m_terminate_capture_thread = false;

#if 0
		m_capture_thread_handle = ::CreateThread(NULL, 0, CaptureTestThreadProc, (void*)this, 0, NULL);
#else
		m_capture_thread_handle = ::CreateThread(NULL, 0, CaptureAndSendThreadProc, (void*)this, 0, NULL);
#endif

		return m_capture_thread_handle != NULL;
	}


	bool ServerController::TerminateCaptureThread(void)
	{
		if(m_capture_thread_handle != NULL){
			m_terminate_capture_thread = true;

			if(::WaitForSingleObject(m_capture_thread_handle, INFINITE) == WAIT_FAILED){
				return false;
			}
			if(::CloseHandle(m_capture_thread_handle) == 0){
				return false;
			}
			m_capture_thread_handle = NULL;
		}

		return true;
	}


	DWORD WINAPI ServerController::CaptureAndSendThreadProc(LPVOID _self){
		bool err;
		DWORD start_time;
		//DWORD t1, t2, t3, t4, t5;
		int zoom_scale;

		//FILE* fp = fopen("profile.csv", "w");

		ServerController* self = (ServerController*)_self;

		while(!self->m_terminate_capture_thread){
			while(!self->m_terminate_capture_thread && !self->m_server_port->SendDataSocketIsOpened()){
				Sleep(500);
			}

			start_time = 0;	
			while(!self->m_terminate_capture_thread && self->m_server_port->SendDataSocketIsOpened()){
				//t1 = ::timeGetTime();
				//diff_time = ::timeGetTime() - start_time;
				do{
					::Sleep(1);
				}while(::timeGetTime() - start_time < 30);
				//if(diff_time < 30){
				//	::Sleep(30 - diff_time);
				//}
				start_time = ::timeGetTime();

				//t2 = ::timeGetTime();
				zoom_scale = self->m_frame_zoom_scale;
				if(self->m_frame_width == 0 || self->m_frame_height == 0){
					continue;
				}
				err |= !self->m_capture->Capture(self->m_frame_x, self->m_frame_y, self->m_frame_width, self->m_frame_height);

				//t3 = ::timeGetTime();
				err |= !self->m_encoder->Encode(
					(char*)self->m_capture->GetCaptureBmpAddress(),
					self->m_capture->GetCaptureBmpWidth(),
					self->m_capture->GetCaptureBmpHeight(),
					self->m_capture->GetCanvasWidth(),
					self->m_capture->GetCanvasHeight(),
					self->m_image_quality
				);

				//t4 = ::timeGetTime();
				err |= !self->m_server_port->SendData(
					self->m_encoder->GetData(),
					self->m_encoder->GetDataSize(),
					self->m_capture->GetCaptureBmpX(),
					self->m_capture->GetCaptureBmpY(),
					self->m_capture->GetCaptureBmpWidth(),
					self->m_capture->GetCaptureBmpHeight(),
					zoom_scale
				);

				err |= !self->m_event_handler->Exec(self->m_frame_ct);

				self->m_frame_ct++;

				//t5 = ::timeGetTime();

				//fprintf(fp, "%d,%d,%d,%d,%d\n", t2 - t1, t3 - t2, t4 - t3, t5 - t4, self->m_encoder->GetDataSize());
			}
		}

		//fclose(fp);

		return 0;
	}


	DWORD WINAPI ServerController::CaptureTestThreadProc(LPVOID _self){
		bool err;
		ServerController* self = (ServerController*)_self;

		//err |= !self->m_capture->Capture(0, 0);
		while(!self->m_terminate_capture_thread){
			err |= !self->m_capture->Capture(self->m_frame_x, self->m_frame_y, self->m_frame_width, self->m_frame_height);
			err |= !self->m_encoder->Encode(
				(char*)self->m_capture->GetCaptureBmpAddress(),
				self->m_capture->GetCaptureBmpWidth(),
				self->m_capture->GetCaptureBmpHeight(),
				self->m_capture->GetCanvasWidth(),
				self->m_capture->GetCanvasHeight(),
				self->m_image_quality,
				self->m_image_resolution
			);
			self->m_encoder->WriteToFile("test.jpg");
			self->m_frame_ct++;
		}

		return 0;
	}


	bool ServerController::DDServerPortChangeState(void)
	{
		PostMessageToObservers(WM_CHANGE_SERVER_STATE, 0, 0);
		return true;
	}

	bool ServerController::DDServerPortCommandSendImageHeader(int image_width, int image_height, int image_scale, const unsigned char **header_buf, int* header_length)
	{
/*		bool result_ok = true;

		m_image_width = image_width;
		m_image_height = image_height;
		m_image_scale = image_scale;

		result_ok &= m_capture->Init(m_image_width, m_image_height);
		result_ok &= m_encoder->Init(m_image_width, m_image_height);

		*header_buf = m_encoder->GetHeader();
		*header_length = m_encoder->GetHeaderSize();

		return result_ok;*/
		return false;
	}

	bool ServerController::DDServerPortCommandDownloadFrame(int x, int y, int width, int height, char **result_buf, DWORD* result_len)
	{
		bool err;
		err |= !m_capture->Capture(x, y, width, height);
		err |= !m_encoder->Encode(
			(char*)m_capture->GetCaptureBmpAddress(),
			m_capture->GetCaptureBmpWidth(),
			m_capture->GetCaptureBmpHeight(),
			m_capture->GetCanvasWidth(),
			m_capture->GetCanvasHeight(),
			m_image_quality,
			m_image_resolution
		);

		*result_buf = (char*)m_encoder->GetData();
		*result_len = m_encoder->GetDataSize();
		m_frame_ct++;
		//m_encoder->WriteToFile("test.jpg");

		return !err;
	}



	bool ServerController::DDServerPortCommandSetDownloadFrame(int x, int y, int width, int height, int zoom_scale)
	{
		m_frame_x = x;
		m_frame_y = y;
		m_frame_width = width;
		m_frame_height = height;
		m_frame_zoom_scale = zoom_scale;
		PostMessageToObservers(WM_CHANGE_SERVER_STATE, 0, 0);
		return true;
	}



	bool ServerController::DDServerPortCommandUIEvent(DDUIEventType type, DDUIEventOwner owner, int param1, int param2)
	{
		return m_event_handler->AddEvent(type, owner, param1, param2);
	}

	bool ServerController::DDServerPortCommandKeyinputs(char* buf, int len)
	{
		int result;
		GUITHREADINFO thread_info;
		HWND focused_hwnd;

		HWND active_window = ::GetForegroundWindow();
		if(active_window == NULL){
			return false;
		}

		DWORD thread_id = ::GetWindowThreadProcessId(active_window, NULL);
		if(thread_id == NULL){
			return false;
		}

		thread_info.cbSize = sizeof(thread_info);
		result = ::GetGUIThreadInfo(thread_id, &thread_info);
		if(result == 0){
			return false;
		}
		focused_hwnd = thread_info.hwndFocus;

		{
			HGLOBAL hData = (HGLOBAL)::GlobalAlloc(GHND, (DWORD)len+1);
			char *pData = (char *)::GlobalLock(hData);
			::memcpy(pData, buf, len);
			pData[len] = '\0';
			::GlobalUnlock(hData);
			::OpenClipboard(NULL);
			::EmptyClipboard();
			if (::SetClipboardData(CF_TEXT, hData) == NULL) {
				::GlobalFree(hData);
				::CloseClipboard();
				return false;
			}
			::CloseClipboard();
		}

		// send return key
		INPUT input[] ={
			{ INPUT_KEYBOARD, VK_CONTROL, 0, 0, 0},
			{ INPUT_KEYBOARD, 'A', 0, 0, 0},
			{ INPUT_KEYBOARD, 'A', KEYEVENTF_KEYUP, 0, 0},
			{ INPUT_KEYBOARD, VK_CONTROL, KEYEVENTF_KEYUP, 0, 0},
			{ INPUT_KEYBOARD, VK_DELETE, 0, 0, 0},
			{ INPUT_KEYBOARD, VK_DELETE, KEYEVENTF_KEYUP, 0, 0},
			{ INPUT_KEYBOARD, VK_CONTROL, 0, 0, 0},
			{ INPUT_KEYBOARD, 'V', 0, 0, 0},
			{ INPUT_KEYBOARD, 'V', KEYEVENTF_KEYUP, 0, 0},
			{ INPUT_KEYBOARD, VK_CONTROL, KEYEVENTF_KEYUP, 0, 0},
			{ INPUT_KEYBOARD, VK_RETURN, 0, 0, 0},
			{ INPUT_KEYBOARD, VK_RETURN, KEYEVENTF_KEYUP, 0, 0}
		};
		result = ::SendInput(sizeof(input) / sizeof(input[0]), input, sizeof(input[0]));

		return result != 0;
	}

	bool ServerController::DDServerPortCommandChangeImageQuality(int quality)
	{
		m_image_quality = quality;
		PostMessageToObservers(WM_CHANGE_SERVER_STATE, 0, 0);
		return true;
	}

	bool ServerController::DDServerPortCommandChangeImageResolution(int resolution)
	{
		m_image_resolution = resolution;
		PostMessageToObservers(WM_CHANGE_SERVER_STATE, 0, 0);
		return true;
	}

}
